package com.lipaluma.field.validator.impl.date;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;

import com.lipaluma.Xml;
import com.lipaluma.annotations.validations.ValidateDate;
import com.lipaluma.context.Context;
import com.lipaluma.exceptions.CsvParserException;
import com.lipaluma.field.validator.ValidationError;
import com.lipaluma.field.validator.Validator;
import com.lipaluma.xml.validator.XmlBeforeValidator;
import com.lipaluma.xml.validator.XmlValidator;

/**
 * Validate a date field to check if the date value from the source is before than the value given.<br/>
 * 
 * <p>
 * For Initialisation :
 * <ul>
 * 	<li>
 * 	<u><b>By annotations :</b></u> Use {@link ValidateDate}, with the attribute <code>before = "2013-10-15"</code>. <br/>
 * Beware ! the attribute <code>pattern</code> is mandatory to resolve the parsing of the given date.
 * </li>
 * 	<li>
 * 	<u><b>By Xml :</b></u>
 * <ul>
 * <li>
 * To add an before validator, use the element <code>before</code> like below : <br/>
 * <code>
 * &lt;ToMap field="..."   ...><br/>
 * &nbsp;&nbsp;&nbsp;<b>&lt;before date="2013-10-15" pattern="yyyy-MM-dd"/></b><br/>
 * &lt;/ToMap>
 * </code>
 * <br/>
 * Take care of the mandatory attribute pattern used to parse the date value given.
 * </li>
 * </li>
 * </ul>
 * @author Mario
 *
 */
public class BeforeDateValidator implements Validator {
	public DateTime date;
	
	public BeforeDateValidator() {}
	/**
	 * Initialisation by a DateTime
	 * @param threshold threshold DateTime
	 */
	public BeforeDateValidator(DateTime threshold) {
		if(threshold == null)
			throw new CsvParserException("The threshold date value on Before Validator cannot be null");
		this.date = threshold;
	}
	/**
	 * Initialisation by a Date
	 * @param threshold threshold DateTime
	 */
	public BeforeDateValidator(Date threshold) {
		if(threshold == null)
			throw new CsvParserException("The threshold date value on Before Validator cannot be null");
		this.date = new DateTime(threshold.getTime());
	}
	
	/**
	 * Initialisation by a Date
	 * @param threshold threshold date
	 */
	public boolean init(Context context, Field field) {
		ValidateDate annotation = field.getAnnotation(ValidateDate.class);
		if(annotation == null || annotation.before().isEmpty())
			return false;
		if(StringUtils.isEmpty(annotation.pattern()))
			throw new CsvParserException("Error on field "+field.getName()+" : The "+Xml.BEFORE+" validator must have a non empty '"+Xml.PATTERN+"'.");
		this.date = DateTimeFormat.forPattern(annotation.pattern()).parseDateTime(annotation.before());
		return true;
	}

	public boolean init(Context context, Field field, XmlValidator xmlValidator) {
		XmlBeforeValidator validator = (XmlBeforeValidator) xmlValidator;
		if(StringUtils.isBlank(validator.getPattern()))
			throw new CsvParserException("Error on "+XmlBeforeValidator.BEFORE+" validation on field "+field.getName()+" : the pattern must be specified.");
		this.date = DateTimeFormat.forPattern(validator.getPattern()).parseDateTime(validator.getDate());
		return true;
	}

	public boolean validate(Object value, List<ValidationError> errors) {
		DateTime dateValue = null;
		if(Date.class.isAssignableFrom(value.getClass()))
			dateValue = new DateTime(((Date)value).getTime());
		else if(DateTime.class.isAssignableFrom(value.getClass()))
				dateValue = (DateTime)value;
		else {
			errors.add(new ValidationError("The value "+value+" can't be casted to a date"));
			return false;
		}
		if(dateValue.isAfter(this.date)) {
			errors.add(new ValidationError("The value Date "+dateValue.toString("yyyy-MM-dd")+" is after "+date.toString("yyyy-MM-dd")+" rather before"));
			return false;
		}
		return true;
	}

}
